#include "StdAfx.h"
#include "VertexFactory.h"

#include "LightMap.h"

using namespace RenderSystem;

VertexFactoryType::VertexFactoryTypes& VertexFactoryType::GetTypeList()
{
	static VertexFactoryTypes rVFTypeList;
	return rVFTypeList;
}

/**
* Finds a VertexFactoryType by name.
*/
VertexFactoryType* VertexFactoryType::GetVFByName(const String& strVFName)
{
	VertexFactoryTypes& VFTypes = VertexFactoryType::GetTypeList();
	for( VertexFactoryTypesIterator itrType = VFTypes.begin(); itrType != VFTypes.end(); ++itrType )
	{
		if( (*itrType)->GetName() == strVFName )
			return *itrType;
	}

	return NULL;
}

VertexFactoryType::VertexFactoryType(
	const String& strInName,
	const String& strInShaderFilename,
	bool bInUsedWithMaterials,
	bool bInSupportsStaticLighting,
	bool bInSupportsDynamicLighting,
	ConstructParametersType InConstructParameters,
	ModifyCompilationEnvironmentType InModifyCompilationEnvironment
	):
	m_strVFName(strInName),
	m_strShaderFilename(strInShaderFilename),
	m_bUsedWithMaterials(bInUsedWithMaterials),
	m_bSupportsStaticLighting(bInSupportsStaticLighting),
	m_bSupportsDynamicLighting(bInSupportsDynamicLighting),
	m_funcConstructParameters(InConstructParameters),
	m_funcModifyCompilationEnvironmentRef(InModifyCompilationEnvironment)
{
	// Add this vertex factory type to the global list.
	if( GetVFByName( strInName ) == NULL )
		GetTypeList().push_back(this);
}

VertexFactory::DataType::DataType()
{
	LightMapStream.Offset = 0;
	LightMapStream.DirectionalStride = sizeof(QuantizedDirectionalLightSample);
	LightMapStream.SimpleStride = sizeof(QuantizedSimpleLightSample);
	LightMapStream.bUseInstanceIndex = FALSE;
	NumInstances = 1;
}

void VertexFactory::Set() const
{
	for(UINT uStreamIndex = 0; uStreamIndex != m_Streams.size(); uStreamIndex++)
	{
		VertexStream* pStream = m_Streams[uStreamIndex];
		RHISetStreamSource( uStreamIndex, pStream->pVertexBuffer, pStream->uStride, pStream->bUseInstanceIndex, m_Data.NumInstances );
	}
}

void VertexFactory::SetPositionStream() const
{
	for(UINT uStreamIndex = 0; uStreamIndex != m_PositionStream.size(); uStreamIndex++)
	{
		VertexStream* pStream = m_PositionStream[uStreamIndex];
		RHISetStreamSource( uStreamIndex, pStream->pVertexBuffer, pStream->uStride, pStream->bUseInstanceIndex, m_Data.NumInstances );
	}
}

void VertexFactory::SetVertexShadowMap(const VertexBuffer* pShadowMapVertexBuffer) const
{
	Set();

	// Set the shadow-map vertex stream
	RHISetStreamSource( m_Streams.size(), pShadowMapVertexBuffer, sizeof(float), FALSE, 1 );
}

void VertexFactory::SetVertexLightMap(const VertexBuffer* pLightMapVertexBuffer, bool bUseDirectionalLightMap) const
{
	Set();
	// Set the shadow-map vertex stream.
	BYTE LightMapStride = bUseDirectionalLightMap ? m_Data.LightMapStream.DirectionalStride : m_Data.LightMapStream.SimpleStride;
	
	RHISetStreamSource( m_Streams.size(), pLightMapVertexBuffer, LightMapStride, m_Data.LightMapStream.bUseInstanceIndex, m_Data.NumInstances );
}

/**
* Activates the vertex factory for rendering a vertex light-map and vertex shadow-map at the same time.
*/
void VertexFactory::SetVertexLightMapAndShadowMap(const VertexBuffer* pLightMapVertexBuffer, const VertexBuffer* pShadowMapVertexBuffer) const
{
	Set();

	// Set the shadow-map vertex stream.
	const BYTE LightMapStride = m_Data.LightMapStream.DirectionalStride;
	RHISetStreamSource( m_Streams.size(), pLightMapVertexBuffer, LightMapStride, m_Data.LightMapStream.bUseInstanceIndex, m_Data.NumInstances );
	
	// Set the shadow-map vertex stream.
	RHISetStreamSource( m_Streams.size() + 1, pShadowMapVertexBuffer, sizeof(float), FALSE, 1 );
}

/**
* Fill in array of strides from this factory's vertex streams without shadow/light maps
* @param OutStreamStrides - output array of # MaxVertexElementCount stream strides to fill
*/
UINT VertexFactory::GetStreamStrides( DWORD* pOutStreamStrides, bool bPadWithZeroes ) const
{
	UINT uStreamIndex = 0;
	for( ; uStreamIndex != m_Streams.size();++uStreamIndex )
	{
		pOutStreamStrides[uStreamIndex] = m_Streams[uStreamIndex]->uStride;
	}

	if( bPadWithZeroes )
	{
		// Pad stream strides with 0's to be safe (they can be used in hashes elsewhere)
		for ( ; uStreamIndex < MaxVertexElementCount; ++uStreamIndex )
		{
			pOutStreamStrides[uStreamIndex] = 0;
		}
	}
	return uStreamIndex;
}

/**
* Fill in array of strides from this factory's position only vertex streams
* @param OutStreamStrides - output array of # MaxVertexElementCount stream strides to fill
*/
void VertexFactory::GetPositionStreamStride(DWORD* pOutStreamStrides) const
{
	UINT uStreamIndex = 0;
	for( ; uStreamIndex != m_Streams.size();++uStreamIndex )
	{
		pOutStreamStrides[uStreamIndex] = m_PositionStream[uStreamIndex]->uStride;
	}

	// Pad stream strides with 0's to be safe (they can be used in hashes elsewhere)
	for ( ; uStreamIndex < MaxVertexElementCount; ++uStreamIndex )
	{
		pOutStreamStrides[uStreamIndex] = 0;
	}
}

/**
* Fill in array of strides from this factory's vertex streams when using shadow-maps
* @param OutStreamStrides - output array of # MaxVertexElementCount stream strides to fill
*/
void VertexFactory::GetVertexShadowMapStreamStrides(DWORD *pOutStreamStrides) const
{
	UINT uStreamIndex = this->GetStreamStrides(pOutStreamStrides, false);

	pOutStreamStrides[ uStreamIndex++ ] = sizeof(float);

	// Pad stream strides with 0's to be safe (they can be used in hashes elsewhere)
	for ( ; uStreamIndex < MaxVertexElementCount; ++uStreamIndex)
	{
		pOutStreamStrides[uStreamIndex] = 0;
	}
}

/**
* Fill in array of strides from this factory's vertex streams when using light-maps
* @param OutStreamStrides - output array of # MaxVertexElementCount stream strides to fill
*/
void VertexFactory::GetVertexLightMapStreamStrides(DWORD* pOutStreamStrides,bool bUseDirectionalLightMap) const
{
	UINT uStreamIndex = this->GetStreamStrides(pOutStreamStrides, false);
	BYTE LightMapStride = bUseDirectionalLightMap ? m_Data.LightMapStream.DirectionalStride : m_Data.LightMapStream.SimpleStride;

	pOutStreamStrides[ uStreamIndex++ ] = LightMapStride;

	// Pad stream strides with 0's to be safe (they can be used in hashes elsewhere)
	for ( ; uStreamIndex < MaxVertexElementCount; ++uStreamIndex)
	{
		pOutStreamStrides[uStreamIndex] = 0;
	}
}

/**
* Fill in array of strides from this factory's vertex streams when using light-maps amd shadow-maps at the same time
* @param OutStreamStrides - output array of # MaxVertexElementCount stream strides to fill
*/
void VertexFactory::GetVertexLightMapAndShadowMapStreamStrides(DWORD* pOutStreamStrides) const
{
	UINT uStreamIndex = GetStreamStrides(pOutStreamStrides, false);
	BYTE LightMapStride = m_Data.LightMapStream.DirectionalStride;
	
	pOutStreamStrides[uStreamIndex++] = LightMapStride;
	pOutStreamStrides[uStreamIndex++] = sizeof(FLOAT);
	// Pad stream strides with 0's to be safe (they can be used in hashes elsewhere)
	for (; uStreamIndex < MaxVertexElementCount;++uStreamIndex)
	{
		pOutStreamStrides[uStreamIndex] = 0;
	}
}

VertexElement VertexFactory::AccessStreamComponent(const VertexStreamComponent& Component,BYTE Usage,BYTE UsageIndex)
{
	std::auto_ptr<VertexStream> ptrVertexStream( new VertexStream );
	ptrVertexStream->pVertexBuffer = Component.pVertexBuffer;
	ptrVertexStream->uStride = Component.Stride;
	ptrVertexStream->bUseInstanceIndex = Component.bUseInstanceIndex;

	UINT uStreamIdx = 0;
	std::vector<VertexStream*>::iterator itrStream = std::find( m_Streams.begin(), m_Streams.end(), ptrVertexStream );
	if( itrStream == m_Streams.end() )
	{
		m_Streams.push_back( ptrVertexStream.release() );
		uStreamIdx = m_Streams.size();
	}
	else
		uStreamIdx = std::distance( m_Streams.begin(), itrStream );

	return VertexElement( uStreamIdx, Component.Offset,Component.Type,Usage,UsageIndex,Component.bUseInstanceIndex);
}

VertexElement VertexFactory::AccessPositionStreamComponent(const VertexStreamComponent& Component,BYTE Usage,BYTE UsageIndex)
{
	std::auto_ptr<VertexStream> ptrVertexStream( new VertexStream );
	ptrVertexStream->pVertexBuffer = Component.pVertexBuffer;
	ptrVertexStream->uStride = Component.Stride;
	ptrVertexStream->bUseInstanceIndex = Component.bUseInstanceIndex;

	UINT uStreamIdx = 0;
	std::vector<VertexStream*>::iterator itrStream = std::find( m_PositionStream.begin(), m_PositionStream.end(), ptrVertexStream );
	if( itrStream == m_PositionStream.end() )
	{
		m_PositionStream.push_back( ptrVertexStream.release() );
		uStreamIdx = m_PositionStream.size();
	}
	else
		uStreamIdx = std::distance( m_PositionStream.begin(), itrStream );

	return VertexElement( uStreamIdx, Component.Offset,Component.Type,Usage,UsageIndex,Component.bUseInstanceIndex);
}

/**
*	GetVertexElementSize returns the size of data based on its type
*	it's needed for FixFXCardDeclarator function
*/
static BYTE GetVertexElementSize( BYTE Type )
{
	switch( Type )
	{
	case VET_Float1:
		return 4;
	case VET_Float2:
		return 8;
	case VET_Float3:
		return 12;
	case VET_Float4:
		return 16;
	case VET_PackedNormal:
	case VET_UByte4:
	case VET_UByte4N:
	case VET_Color:
	case VET_Short2:
	case VET_Short2N:
	case VET_Half2:
		return sizeof 4;
	default:
		return 0;
	}
}

void VertexFactory::InitDeclaration(
	const VertexDeclarationElementList& rElements, 
	const DataType& rData, 
	bool bCreateShadowMapDeclaration , 
	bool bCreateDirectionalLightMapDeclaration , 
	bool bCreateSimpleLightMapDeclaration )
{
	// Make a copy of the vertex factory data.
	m_Data = rData;

	// Create the vertex declaration for rendering the factory normally.
	m_Declaration = RHICreateVertexDeclaration( rElements );

	if (GetType()->SupportsStaticLighting())
	{
		if (bCreateShadowMapDeclaration)
		{
			// Create the vertex declaration for rendering the factory with a vertex shadow-map.
			VertexDeclarationElementList shadowMapElements = rElements;
			shadowMapElements.push_back( VertexElement(m_Streams.size(), 0, VET_Float1, VEU_BlendWeight, 0) );
			m_VertexShadowMapDeclaration = RHICreateVertexDeclaration(shadowMapElements);
		}

		if (bCreateDirectionalLightMapDeclaration)
		{
			// Create the vertex declaration for rendering the factory with a directional vertex light-map.
			VertexDeclarationElementList LightMapElements = rElements;

			for(INT CoefficientIndex = 0;CoefficientIndex < NUM_DIRECTIONAL_LIGHTMAP_COEF;CoefficientIndex++)
			{
				LightMapElements.push_back(
					VertexElement(
					m_Streams.size(),
					m_Data.LightMapStream.Offset + ( sizeof(QuantizedDirectionalLightSample) / NUM_DIRECTIONAL_LIGHTMAP_COEF ),
					VET_Color,
					VEU_TextureCoordinate,
					5 + CoefficientIndex,
					m_Data.LightMapStream.bUseInstanceIndex
					));
			}
			m_DirectionalVertexLightMapDeclaration = RHICreateVertexDeclaration(LightMapElements);

			LightMapElements.push_back( VertexElement(m_Streams.size() + 1, 0, VET_Float1, VEU_BlendWeight, 0) );
			m_DirectionalVertexLightMapAndShadowMapDeclaration = RHICreateVertexDeclaration(LightMapElements);
		}

		if (bCreateSimpleLightMapDeclaration)
		{
			// Create the vertex declaration for rendering the factory with a simple vertex light-map.
			VertexDeclarationElementList LightMapElements = rElements;
			LightMapElements.push_back( 
				VertexElement(
				m_Streams.size(),
				m_Data.LightMapStream.Offset + ( sizeof(QuantizedSimpleLightSample) / NUM_SIMPLE_LIGHTMAP_COEF ),
				VET_Color,
				VEU_TextureCoordinate,
				5,
				m_Data.LightMapStream.bUseInstanceIndex
				));

			m_SimpleVertexLightMapDeclaration = RHICreateVertexDeclaration(LightMapElements);
		}
	}
}

void VertexFactory::InitPositionDeclaration(const VertexDeclarationElementList& rElements)
{
	// Create the vertex declaration for rendering the factory normally.
	m_PositionDeclaration = RHICreateVertexDeclaration(rElements);
}
